<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by an Apache 2.0 License.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.testing.MockClassFactory</title>
<script src="../base.js"></script>
<script>
  goog.require('goog.testing.MockClassFactory');
  goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>
  goog.provide('fake.BaseClass');
  goog.provide('fake.ChildClass');

  // Classes that will be mocked.  A base class and child class are used to
  // test inheritance.
  fake.BaseClass = function(a) {
    fail('real object should never be called');
  };

  fake.BaseClass.prototype.foo = function() {
    fail('real object should never be called');
  };

  fake.BaseClass.prototype.toString = function() {return 'foo';}

  fake.BaseClass.prototype.toLocaleString = function() {return 'bar';}

  fake.ChildClass = function(a) {
    fail('real object should never be called');
  };
  goog.inherits(fake.ChildClass, fake.BaseClass);

  fake.ChildClass.staticFoo = function() {
    fail('real object should never be called');
  };

  fake.ChildClass.prototype.bar = function() {
    fail('real object should never be called');
  };

  fake.ChildClass.staticProperty = 'staticPropertyOnClass';

  function TopLevelBaseClass() {
  }

  var mockClassFactory = new goog.testing.MockClassFactory();
  var matchers = goog.testing.mockmatchers;

  function tearDown() {
    mockClassFactory.reset();
  };

  function testGetStrictMockClass() {
    var mock1 = mockClassFactory.getStrictMockClass(fake, fake.BaseClass, 1);
    mock1.foo();
    mock1.$replay();

    var mock2 = mockClassFactory.getStrictMockClass(fake, fake.BaseClass, 2);
    mock2.foo();
    mock2.$replay();

    var mock3 = mockClassFactory.getStrictMockClass(fake, fake.ChildClass, 3);
    mock3.foo();
    mock3.bar();
    mock3.$replay();

    var instance1 = new fake.BaseClass(1);
    instance1.foo();
    mock1.$verify();

    var instance2 = new fake.BaseClass(2);
    instance2.foo();
    mock2.$verify();

    var instance3 = new fake.ChildClass(3);
    instance3.foo();
    instance3.bar();
    mock3.$verify();

    assertThrows(function() {new fake.BaseClass(-1)});
    assertTrue(instance1 instanceof fake.BaseClass);
    assertTrue(instance2 instanceof fake.BaseClass);
    assertTrue(instance3 instanceof fake.ChildClass);
  };

  function testGetStrictMockClassCreatesAllProxies() {
    var mock1 = mockClassFactory.getStrictMockClass(fake, fake.BaseClass, 1);
    // toString(), toLocaleString() and others are treaded specially in
    // createProxy_().
    mock1.toString();
    mock1.toLocaleString();
    mock1.$replay();

    var instance1 = new fake.BaseClass(1);
    instance1.toString();
    instance1.toLocaleString();
    mock1.$verify();
  }

  function testGetLooseMockClass() {
    var mock1 = mockClassFactory.getLooseMockClass(fake, fake.BaseClass, 1);
    mock1.foo().$anyTimes().$returns(3);
    mock1.$replay();

    var mock2 = mockClassFactory.getLooseMockClass(fake, fake.BaseClass, 2);
    mock2.foo().$times(3);
    mock2.$replay();

    var mock3 = mockClassFactory.getLooseMockClass(fake, fake.ChildClass, 3);
    mock3.foo().$atLeastOnce().$returns(5);
    mock3.bar().$atLeastOnce();
    mock3.$replay();

    var instance1 = new fake.BaseClass(1);
    assertEquals(3, instance1.foo());
    assertEquals(3, instance1.foo());
    assertEquals(3, instance1.foo());
    assertEquals(3, instance1.foo());
    assertEquals(3, instance1.foo());
    mock1.$verify();

    var instance2 = new fake.BaseClass(2);
    instance2.foo();
    instance2.foo();
    instance2.foo();
    mock2.$verify();

    var instance3 = new fake.ChildClass(3);
    assertEquals(5, instance3.foo());
    assertEquals(5, instance3.foo());
    instance3.bar();
    mock3.$verify();

    assertThrows(function() {new fake.BaseClass(-1)});
    assertTrue(instance1 instanceof fake.BaseClass);
    assertTrue(instance2 instanceof fake.BaseClass);
    assertTrue(instance3 instanceof fake.ChildClass);
  };

  function testGetStrictStaticMock() {
    var staticMock = mockClassFactory.getStrictStaticMock(fake,
        fake.ChildClass);
    var mock = mockClassFactory.getStrictMockClass(fake, fake.ChildClass, 1);

    mock.foo();
    mock.bar();
    staticMock.staticFoo();
    mock.$replay();
    staticMock.$replay();

    var instance = new fake.ChildClass(1);
    instance.foo();
    instance.bar();
    fake.ChildClass.staticFoo();
    mock.$verify();
    staticMock.$verify();

    assertTrue(instance instanceof fake.BaseClass);
    assertTrue(instance instanceof fake.ChildClass);
    assertThrows(function() {
      mockClassFactory.getLooseStaticMock(fake, fake.ChildClass)
    });
  };

  function testGetStrictStaticMockKeepsStaticProperties() {
    var OriginalChildClass = fake.ChildClass;
    var staticMock = mockClassFactory.getStrictStaticMock(fake,
        fake.ChildClass);
    assertEquals(OriginalChildClass.staticProperty,
        fake.ChildClass.staticProperty);
  };

  function testGetLooseStaticMockKeepsStaticProperties() {
    var OriginalChildClass = fake.ChildClass;
    var staticMock = mockClassFactory.getLooseStaticMock(fake,
        fake.ChildClass);
    assertEquals(OriginalChildClass.staticProperty,
        fake.ChildClass.staticProperty);
  };

  function testGetLooseStaticMock() {
    var staticMock = mockClassFactory.getLooseStaticMock(fake,
        fake.ChildClass);
    var mock = mockClassFactory.getStrictMockClass(fake, fake.ChildClass, 1);

    mock.foo();
    mock.bar();
    staticMock.staticFoo().$atLeastOnce();
    mock.$replay();
    staticMock.$replay();

    var instance = new fake.ChildClass(1);
    instance.foo();
    instance.bar();
    fake.ChildClass.staticFoo();
    fake.ChildClass.staticFoo();
    mock.$verify();
    staticMock.$verify();

    assertTrue(instance instanceof fake.BaseClass);
    assertTrue(instance instanceof fake.ChildClass);
    assertThrows(function() {
      mockClassFactory.getStrictStaticMock(fake, fake.ChildClass)
    });
  };

  function testFlexibleClassMockInstantiation() {
    // This mock should be returned for all instances created with a number
    // as the first argument.
    var mock = mockClassFactory.getStrictMockClass(fake, fake.ChildClass,
        matchers.isNumber);
    mock.foo(); // Will be called by the first mock instance.
    mock.foo(); // Will be called by the second mock instance.
    mock.$replay();

    var instance1 = new fake.ChildClass(1);
    var instance2 = new fake.ChildClass(2);
    instance1.foo();
    instance2.foo();
    assertThrows(function() {
      new fake.ChildClass('foo');
    });
    mock.$verify();
  };

  function testMockTopLevelClass() {
    var mock = mockClassFactory.getStrictMockClass(goog.global,
        goog.global.TopLevelBaseClass);
  }
</script>
</body>
</html>
